home *** CD-ROM | disk | FTP | other *** search
- #include "Painterly.h"
-
- /* Painterly Globals */
- extern DocumentRecord gSrcDoc, gDstDoc; /* The doc records */
- extern CWindowPtr gSrcWindPtr, gDstWindPtr, gUndoTarget;
- extern Handle gCurrentBrushHandle; /* handle to the current Brush code */
- extern BrushParams gBrushStuff; /* the parameter structure for brush calls */
- extern GWorldPtr gUndoBuffer; /* a buffer to allow undo */
- extern RGBColor gBGColor;
-
- /* These globals are used only by the pict spooling procedures */
- extern OSErr gPictError;
- extern long gPictSize;
- extern PicHandle gPictHandle;
- extern short gPictFileRef;
-
-
- /* Returns true if the given window is one of ours */
- Boolean IsAppWindow(WindowPtr wind)
- {
- Boolean rslt;
-
- if(wind == gSrcWindPtr || wind == gDstWindPtr)
- rslt = true;
- else
- rslt = false;
-
- return rslt;
- }
-
-
- /* Paints a brush stroke both in the offscreen world and in the destination window.
- The point passed in here is in the GWorld's coordinates, so it needs to be converted
- to the window's coordinates when we draw there */
- void StrokeBoth(Point pt)
- {
- short ignoredErr;
- CGrafPtr oldport;
- GDHandle olddev;
- long seed;
- DocumentPeek doc;
-
- GetGWorld(&oldport, &olddev);
- if(LockBoth())
- {
- /* Install the point */
- gBrushStuff.pt = pt;
-
- /* Get the color from the source */
- SetGWorld(gSrcDoc.world, nil);
- GetCPixel(pt.h, pt.v, &gBrushStuff.color);
-
- /* get the current seed */
- seed = randSeed;
-
- /* Set port to Offworld and draw */
- SetGWorld(gDstDoc.world, nil);
- ignoredErr = CallBrush(kDoStroke, &gBrushStuff, gCurrentBrushHandle);
-
- /* Set port to screen, reset Random seed, change origin momentarily, and draw again */
- SetGWorld(gDstWindPtr, olddev);
- randSeed = seed;
- doc = (DocumentPeek)gDstWindPtr;
- SetOrigin(GetCtlValue(doc->hScroll), GetCtlValue(doc->vScroll));
- CloseClip(gDstWindPtr); /* Set Clip to window minus scrollBars */
- ignoredErr = CallBrush(kDoStroke, &gBrushStuff, gCurrentBrushHandle);
- SetOrigin(0, 0);
- ClipRect(&gDstWindPtr->portRect); /* Reset Clip */
-
- /* Unlock everything */
- UnlockBoth();
-
- /* Set dirty flag */
- gDstDoc.dirty = true;
- }
- SetGWorld(oldport, olddev);
- }
-
- /* Copies a GWorld to its associated window */
- void OffToWindow(CWindowPtr wind, Rect *rect)
- {
- GWorldPtr world;
- Rect srcRect;
- Point srcOrigin;
-
- SetPort(wind);
-
- /* Get the the offscreen coordinates of the top left of the window
- from the scrollBars. */
- srcOrigin.h = GetCtlValue( ((DocumentPeek)wind)->hScroll );
- srcOrigin.v = GetCtlValue( ((DocumentPeek)wind)->vScroll );
-
- /* Get the offscreen rect that needs to be copied */
- srcRect = *rect;
- OffsetRect(&srcRect, srcOrigin.h, srcOrigin.v);
-
- world = ((DocumentPeek)wind)->world;
- if(world != nil)
- {
- if(LockWorld(world))
- {
- ForeColor(blackColor);
- BackColor(whiteColor);
- CopyBits(*GetGWorldPixMap(world), *(wind->portPixMap),
- &srcRect, rect, srcCopy, nil);
- UnlockWorld(world);
- }
- }
- }
-
- /* Copies one GWorld to another */
- void OffToOff(GWorldPtr src, GWorldPtr dst)
- {
- CGrafPtr oldport;
- GDHandle olddev;
-
- if(src == nil || dst == nil)
- return;
-
- GetGWorld(&oldport, &olddev);
- SetGWorld(dst, nil);
- if(LockBoth())
- {
- ForeColor(blackColor);
- BackColor(whiteColor);
- CopyBits(*GetGWorldPixMap(src), *GetGWorldPixMap(dst),
- &src->portRect, &dst->portRect, srcCopy, nil);
- UnlockBoth();
- }
- SetGWorld(oldport, olddev);
- }
-
- /* Erases a GWorld. If it's the destination, it paints it with the background color */
- void EraseOff(GWorldPtr world)
- {
- CGrafPtr oldport;
- GDHandle olddev;
-
- GetGWorld(&oldport, &olddev);
- SetGWorld(world, nil);
- if(LockWorld(world))
- {
- if(world == gSrcDoc.world)
- EraseRect(&world->portRect);
- else
- {
- RGBForeColor(&gBGColor);
- PaintRect(&world->portRect);
- }
- UnlockWorld(world);
- }
- SetGWorld(oldport, olddev);
- }
-
- /* Draws a picture into a GWorld */
- void DrawPictOff(GWorldPtr world, PicHandle pict)
- {
- CGrafPtr oldport;
- GDHandle olddev;
- Rect frame;
-
- if(pict == nil || world == nil)
- return;
-
- frame = (*pict)->picFrame;
- OffsetRect(&frame, -frame.left, -frame.top);
- GetGWorld(&oldport, &olddev);
- SetGWorld(world, nil);
- if(LockWorld(world))
- {
- DrawPicture(pict, &frame);
- UnlockWorld(world);
- }
- SetGWorld(oldport, olddev);
- }
-
- /* Locks a GWorld's Pixels */
- Boolean LockWorld(GWorldPtr world)
- {
- PixMapHandle pix;
-
- pix = GetGWorldPixMap(world);
- return(LockPixels(pix));
- }
-
- /* Unlocks a GWorld's Pixels */
- void UnlockWorld(GWorldPtr world)
- {
- PixMapHandle pix;
-
- pix = GetGWorldPixMap(world);
- UnlockPixels(pix);
- }
-
- /* Locks both our GWorld's Pixels */
- Boolean LockBoth(void)
- {
- Boolean rslt = false;
-
- if(LockWorld(gSrcDoc.world))
- if(LockWorld(gDstDoc.world))
- rslt = true;
- else
- UnlockWorld(gSrcDoc.world);
-
- return rslt;
- }
-
- /* Unlocks both our GWorld's Pixels */
- void UnlockBoth(void)
- {
- UnlockWorld(gSrcDoc.world);
- UnlockWorld(gDstDoc.world);
- }
-
- /* This routine gets any pending activate and update events from the queue and processes
- them. Used when saving changes, when a window is brought to the front behind a dialog */
- void DoActivateUpdate(void)
- {
- EventRecord event;
- GrafPtr oldport;
-
- /* First do activates */
- while(GetNextEvent(activMask, &event) == true)
- {
- AppActivate((WindowPtr)event.message, event.modifiers & activeFlag);
- }
-
- /* Then updates */
- while(GetNextEvent(updateMask, &event) == true)
- {
- GetPort(&oldport);
- SetPort((WindowPtr)event.message);
- BeginUpdate((WindowPtr)event.message);
-
- AppUpdate(&event);
-
- EndUpdate((WindowPtr)event.message);
- SetPort(oldport);
- }
- }
-
-
- /* Stolen from Craig Prouse's famous DoWZoom routine, and modified for my own devious
- purposes. I tried to copy the Finder's behavior: If I can just size the window bigger
- and the result will stay completely on the same device the window is already on, I do
- that. Otherwise the window is zoomed to the top left of the appropriate device. This
- routine assumes up front that we are running in a Color Quickdraw environment, i.e
- there is the possibility of multiple devices. It does NOT zoom the window, it just
- calculates the appropriate rect and installs it in the window's stdState field */
- void ReadyWZoom(WindowPtr wind, short zoomDir, short maxH, short maxV)
- {
- /* Only if we are zooming out do we need to go through all these gyrations */
- if(zoomDir == inZoomOut)
- {
- Rect globalPortRect, theSect, zoomRect, deviceRect;
- GDHandle nthDevice, dominantGDevice;
- long sectArea, greatestArea;
- Boolean oneDevice;
-
- globalPortRect = wind->portRect;
- LocalToGlobal(&topLeft(globalPortRect));
- LocalToGlobal(&botRight(globalPortRect));
-
- /* Walk the device list looking for the device that contains most of the
- window, or all of the window */
- nthDevice = GetDeviceList();
- greatestArea = 0;
- while(nthDevice != nil)
- {
- SectRect(&globalPortRect, &(*nthDevice)->gdRect, &theSect);
-
- /* if the intersection is equal to the portRect, then the window is fully
- contained on this device: we can stop looking */
- if(EqualRect(&globalPortRect, &theSect))
- {
- dominantGDevice = nthDevice;
- break;
- }
- sectArea = (long)(theSect.right - theSect.left) * (theSect.bottom - theSect.top);
- if(sectArea > greatestArea)
- {
- greatestArea = sectArea;
- dominantGDevice = nthDevice;
- }
- nthDevice = GetNextDevice(nthDevice);
- }
-
- /* Now let's see if we can just increase the window's size to the max and stay
- completely on the dominantGDevice */
- SetRect(&zoomRect, globalPortRect.left, globalPortRect.top,
- globalPortRect.left + maxH, globalPortRect.top + maxV);
- deviceRect = (*dominantGDevice)->gdRect;
- InsetRect(&deviceRect, 4, 4); /* Keep window 4 pixels away from edges */
- SectRect(&zoomRect, &deviceRect, &theSect);
-
- /* if the intersection is equal to the zoomRect, then the zoomRect is fully
- contained on this device: we have our new zoomRect. Otherwise we need to
- do a little more work. */
- if(!EqualRect(&zoomRect, &theSect))
- {
- short bias;
-
- /* Calculate the height of the window's title bar. Actually, in this app
- I already have, but in the interests of easy cut and paste I'll do
- it again */
- bias = globalPortRect.top - 1 - (*(((WindowPeek)wind)->strucRgn))->rgnBBox.top;
-
- /* Now we can create our zoom rectangle. Account for MBarHeight if on the
- main device */
- if(dominantGDevice == GetMainDevice())
- bias += GetMBarHeight();
- zoomRect.left = deviceRect.left;
- zoomRect.top = deviceRect.top + bias;
-
- /* Set right and bottom to either the max size or the deviceRect,
- whichever is less */
- if(zoomRect.left + maxH > deviceRect.right)
- zoomRect.right = deviceRect.right;
- else
- zoomRect.right = zoomRect.left + maxH;
-
- if(zoomRect.top + maxV > deviceRect.bottom)
- zoomRect.bottom = deviceRect.bottom;
- else
- zoomRect.bottom = zoomRect.top + maxV;
- }
-
- /* Load the rect into the stdState of the window */
- (*(WStateDataHandle)(((WindowPeek)wind)->dataHandle))->stdState = zoomRect;
- }
- }
-
- /* Releases the GWorlds, and sets them to nil */
- void KillGlobalGWorlds(void)
- {
- /* Need to do this in reverse order (I think), since the Dst and Undo Buffer
- share a device with the Src. */
- if(gUndoBuffer != nil)
- {
- DisposeGWorld(gUndoBuffer);
- gUndoBuffer = nil;
- }
- if(gDstDoc.world != nil)
- {
- DisposeGWorld(gDstDoc.world);
- gDstDoc.world = nil;
- }
- if(gSrcDoc.world != nil)
- {
- DisposeGWorld(gSrcDoc.world);
- gSrcDoc.world = nil;
- }
- }
-
- /* Attempts to allocate the needed GWorlds */
- Boolean InitGlobalGWorlds(Rect *frame)
- {
- CGrafPtr oldport;
- GDHandle olddev;
- short offdepth, rslt = false;
- QDErr err;
- Rect newRect;
-
- GetGWorld(&oldport, &olddev);
-
- KillGlobalGWorlds();
-
- /* Ensure that topLeft is 0, 0 */
- newRect = *frame;
- OffsetRect(&newRect, -newRect.left, -newRect.top);
-
- /* Try for the deepest depth we can get */
- offdepth = 32;
- while(true)
- {
- err = NewGWorld(&gSrcDoc.world, offdepth, &newRect, nil, nil, 0L);
- if(err == noErr)
- {
- err = NewGWorld(&gDstDoc.world, offdepth, &newRect, nil,
- GetGWorldDevice(gSrcDoc.world), noNewDevice);
- if((err == noErr))
- {
- rslt = true;
- break;
- }
- else
- DisposeGWorld(gSrcDoc.world); /* First one worked, so kill it */
- }
- /* If we are here, there was a problem. Reduce offdepth and try again */
- offdepth >>= 1;
- if(offdepth < 1) /* Tried everything, no go */
- break;
- }
- if(rslt == true) /* Success! */
- {
- /* Alert user if shallow depth */
- if(offdepth < 32)
- DepthAlert(offdepth);
-
- /* Clear the worlds */
- EraseOff(gSrcDoc.world);
- EraseOff(gDstDoc.world);
-
- /* Try to recreate the Undo buffer */
- if( NewGWorld( &gUndoBuffer,
- offdepth,
- &newRect,
- nil,
- GetGWorldDevice(gSrcDoc.world),
- noNewDevice) == noErr)
- {
- EraseOff(gUndoBuffer);
- }
- else
- {
- gUndoBuffer = nil;
- DoErrorAlert(kNoUndoStr, 0);
- }
- }
- SetGWorld(oldport, olddev);
- return rslt;
- }
-
- /* Converts a GWorld to a PICT */
- PicHandle WorldToPict(GWorldPtr world)
- {
- CGrafPtr oldport;
- GDHandle olddev;
- PicHandle pict = nil;
-
- if(world == nil)
- return nil;
-
- GetGWorld(&oldport, &olddev);
- SetGWorld(world, nil);
-
- if(LockWorld(world))
- {
- ForeColor(blackColor);
- pict = OpenPicture(&world->portRect);
- CopyBits(*GetGWorldPixMap(world), *GetGWorldPixMap(world),
- &world->portRect, &world->portRect, srcCopy, nil);
- ClosePicture();
- UnlockWorld(world);
- }
- SetGWorld(oldport, olddev);
-
- /* Check for a bad pict */
- if((**pict).picSize > 0)
- return pict;
- else
- {
- KillPicture(pict);
- return nil;
- }
- }
-
- /* Sets the clip of the window to exclude the scroll bars. Assumes the port is set
- already */
- void CloseClip(WindowPtr wind)
- {
- Rect clip;
-
- clip = wind->portRect;
- clip.right -= kScrollAdjust;
- clip.bottom -= kScrollAdjust;
- ClipRect(&clip);
- }
-
- /*---------------------------------------------------------------------------
- Scroll Bar routines: Mostly stolen from TESample, but heavily modified,
- so even their mother wouldn't know 'em.
- ---------------------------------------------------------------------------*/
-
- /* Here we resize the scrollbars (if needed) and adjust their values. It's a good
- idea to hide the controls before calling this routine, otherwise
- it'll probably be pretty ugly for the user */
- void AdjustScrollbars(WindowPtr wind, Boolean needsResize)
- {
- DocumentPeek doc;
-
- doc = (DocumentPeek)wind;
-
- /* First move and resize the bars, if needed */
- if ( needsResize )
- {
- /* First the horizontal one. Scroll bars actually hang over the edge of the
- window by one pixel, for some sick reason known only to the people who invented
- it. So every Mac programmer on earth has to discover this somehow (I did it by
- trial and error). Why isn't this in Inside Mac?) */
-
- MoveControl(doc->hScroll, -1, wind->portRect.bottom - kScrollAdjust);
- SizeControl(doc->hScroll, (wind->portRect.right -
- wind->portRect.left) - (kScrollAdjust - kScrollTweak),
- kScrollWidth);
-
- /* Now the vertical one */
- MoveControl(doc->vScroll, wind->portRect.right - kScrollAdjust, -1);
- SizeControl(doc->vScroll, kScrollWidth, (wind->portRect.bottom -
- wind->portRect.top) - (kScrollAdjust - kScrollTweak));
- }
-
- /* Now adjust the values of the scrollbars */
- AdjustScrollValues(wind);
- }
-
- /* Adjusts the values of the scroll bars. */
- void AdjustScrollValues(WindowPtr wind)
- {
- short lines, max;
- short oldValue;
- Rect viewRect;
- DocumentPeek doc;
-
- doc = (DocumentPeek)wind;
-
- /* viewRect is the port of the window minus the scrollbars */
- viewRect = wind->portRect;
- viewRect.right -= kScrollAdjust;
- viewRect.bottom -= kScrollAdjust;
-
- /* First Vertical */
- oldValue = GetCtlValue(doc->vScroll);
- /* lines is the number of lines (pixels) in the offscreen world */
- lines = doc->world->portRect.bottom;
- /* max is the amount of the picture that can't be seen */
- max = lines - viewRect.bottom;
- if ( max < 0 ) max = 0;
- SetCtlMax(doc->vScroll, max);
-
- /* Adjust scroll value if necessary. It's necessary only if the current control
- value (which is also the number of lines scrolled off the top) plus the visible
- vertical lines in the window is greater than the total picture height. If so,
- then just set the control value to the max, i.e. scrolled all the way down. */
- if(oldValue + viewRect.bottom > lines)
- SetCtlValue(doc->vScroll, max);
-
- /* Now Horizontal */
- oldValue = GetCtlValue(doc->hScroll);
- /* lines is the number of horiz. pixels in the offscreen world */
- lines = doc->world->portRect.right;
- /* max is the amount of the picture that can't be seen */
- max = lines - viewRect.right;
- if ( max < 0 ) max = 0;
- SetCtlMax(doc->hScroll, max);
-
- /* Adjust scroll value if necessary. It's necessary only if the current control
- value (which is also the number of lines scrolled off the left) plus the visible
- horiz. lines in the window is greater than the total picture width. If so,
- then just set the control value to the max, i.e. scrolled all the way right. */
- if(oldValue + viewRect.right > lines)
- SetCtlValue(doc->hScroll, max);
- }
-
-
-
- /* Determines how much to change the value of the scrollbar by and how
- much to scroll the Pict, and then does both */
- pascal void ScrollActionProc(ControlHandle control, short part)
- {
- short amount, value, max;
- WindowPtr window;
-
- if ( part != 0 ) { /* if it was actually in the control */
- window = (*control)->contrlOwner;
- switch ( part ) {
- case inUpButton:
- case inDownButton: /* one line */
- amount = kLineScrollAmount;
- break;
- case inPageUp: /* one page */
- case inPageDown:
- amount = kPageScrollAmount;
- break;
- }
- if ( (part == inDownButton) || (part == inPageDown) )
- amount = -amount; /* reverse direction for a downer */
-
- /* Pin the amount to the scroll bar range, set the value, and calculate the
- change in value */
- value = GetCtlValue(control); /* get current value */
- max = GetCtlMax(control); /* and maximum value */
- amount = value - amount;
- if ( amount < 0 )
- amount = 0;
- else if ( amount > max )
- amount = max;
- SetCtlValue(control, amount);
- amount = value - amount; /* calculate the real change */
-
- /* Scroll the pict the appropriate amount */
- if ( amount != 0 )
- {
- if(control == ((DocumentPeek)window)->hScroll)
- ScrollPict(amount, 0, window);
- else
- ScrollPict(0, amount, window);
- }
- }
- } /* ScrollActionProc */
-
- /* Scrolls the bits in the window by the given amount. */
- void ScrollPict(short hAmnt, short vAmnt, WindowPtr wind)
- {
- RgnHandle updateRgn;
- Rect viewRect;
-
- /* Make a new Region... */
- updateRgn = NewRgn();
-
- /* ...get the rect of the window, minus the scrollBars... */
- viewRect = wind->portRect;
- viewRect.right -= kScrollAdjust;
- viewRect.bottom -= kScrollAdjust;
-
- /* Scroll the window, saving the region that needs to be re-filled */
- ScrollRect(&viewRect, hAmnt, vAmnt, updateRgn);
-
- /* Get the rect that encloses the region and draw to it */
- if(updateRgn != nil)
- {
- viewRect = (*updateRgn)->rgnBBox;
- DisposeRgn(updateRgn);
- }
- OffToWindow(wind, &viewRect);
- }
-
-
- /* Sets up an undo by copying the current window pixels to the Undo buffer and setting
- the gUndoTarget to the window */
- void SetUpForUndo(CWindowPtr wind)
- {
- if(gUndoBuffer != nil)
- {
- gUndoTarget = wind;
- OffToOff(((DocumentPeek)wind)->world, gUndoBuffer);
- }
- }
-
- /* This routine replaces the given string with a string from the resource file */
- Str255 *TheStr(Str255 str, short index)
- {
- GetIndString(str, kAppStringsID, index);
- return str;
- }
-
- /* This routine concatenates 2 pascal strings, adding a space between if addSpace is true */
- Str255 *PStrCat(Str255 str1, Str255 str2, Boolean addSpace)
- {
- unsigned char len1, len2, total;
-
- /* check lengths */
- len1 = str1[0];
- len2 = str2[0];
- total = len1 + len2 + (addSpace ? 1 : 0);
- if(total > 255)
- return nil;
-
- if(addSpace)
- {
- str1[len1 + 1] = 0x20; /* Add the space */
- len1 += 1;
- }
- BlockMove(&str2[1], &str1[len1 + 1], (long)len2);
- str1[0] = total;
- return str1;
- }
-
- /* Just copies one string into another */
- void PStrCopy(Str255 src, Str255 dst)
- {
- BlockMove(src, dst, (long)(src[0] + 1));
- }
-
- void DoErrorAlert(short stringID1, short error)
- {
- Str255 string1, string2;
-
- GetIndString(string1, kErrorStringsID, stringID1);
- if(error != 0)
- NumToString((long)error, string2);
- else
- *string2 = 0;
-
- ParamText(string1, string2, "\p", "\p");
- StopAlert(kErrorAlertID, nil);
- }
-
- /* -----------------------------------------------------
- Replacement bottlenecks for spooling Picts
- ------------------------------------------------------ */
-
- pascal void PictWriter(Ptr pointer, short nextHunk)
- {
- long hunkSize;
-
- if(gPictError == noErr)
- {
- hunkSize = nextHunk;
- gPictError = FSWrite(gPictFileRef, &hunkSize, pointer);
- gPictSize += hunkSize;
- if(gPictHandle != nil)
- (**gPictHandle).picSize = LoWord(gPictSize);
- }
- }
-
- pascal void PictReader(Ptr pointer, short nextHunk)
- {
- long hunkSize;
-
- if(gPictError == noErr)
- {
- hunkSize = nextHunk;
- gPictError = FSRead(gPictFileRef, &hunkSize, pointer);
- /* IM says it is unclear how to handle errors here. Who am I to argue? */
- }
- }
-